home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Programming / SWI / source / src / pl-dde.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-02-18  |  14.1 KB  |  550 lines

  1. /*  $Id: pl-dde.c,v 1.13 1998/02/18 13:56:45 jan Exp $
  2.  
  3.     Part of SWI-Prolog.
  4.     Contributed by Dwig (dwig@markv.com)
  5.     Copying policy: ???
  6.  
  7.     Purpose: Windows DDE interface (client side)
  8. */
  9.  
  10. #if defined(__WINDOWS__) || defined(__WIN32__)
  11.  
  12. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  13.    Extension of SWI-Prolog:
  14.    Primitives to support interprocess communication via DDE, for those
  15.    platforms that support DDEML.  Currently, this is the Windows family (3.1
  16.    and above) and Unix platforms with Bristol's Windows support.
  17.  
  18.    Eventually, this should turn into a full DDE capability.  For the
  19.    present, I'm just implementing the client side of conversation
  20.    management, and only providing request transactions, as follows:
  21.  
  22.    open_dde_conversation(+Service, +Topic, -Handle)
  23.    Open a conversation with a server supporting the given service name and
  24.    topic (atoms).  If successful, Handle may be used to send transactions to
  25.    the server.  If no willing server is found, fails.
  26.  
  27.    close_dde_conversation(+Handle)
  28.    Close the conversation associated with Handle.  All opened conversations
  29.    should be closed when they're no longer needed, although the system
  30.    will close any that remain open on process termination.
  31.  
  32.    dde_request(+Handle, +Item, -Value)
  33.    Request a value from the server.  Item is an atom that identifies the
  34.    requested data, and Value will be an atom (CF_TEXT data in DDE parlance)
  35.    representing that data, if the request is successful.  If unsuccessful,
  36.    Value will be unified with a term of form error(reason), identifying the
  37.    problem.
  38.  
  39.    It could be argued that the atoms above should be strings; I may go that
  40.    way sometime.
  41. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  42.  
  43. #define INCLUDE_DDEML_H
  44. #include "windows.h"
  45. #ifdef __LCC__
  46. #include "ddeml.h"
  47. #endif
  48. #include "pl-incl.h"
  49.  
  50. #if O_DDE
  51. #include <string.h>
  52.  
  53. #ifdef __WATCOMC__            /* at least version 9.5 */
  54. #define WATCOM_DDEACCESS_BUG 1
  55. #endif
  56.  
  57. #define FASTBUFSIZE 512            /* use local buffer upto here */
  58. #define MAX_CONVERSATIONS 32        /* Max. # of conversations */
  59. #define TIMEOUT_VERY_LONG 0x7fffffff;    /* largest positive int */
  60.  
  61. static HCONV conv_handle[MAX_CONVERSATIONS];
  62. static HCONV server_handle[MAX_CONVERSATIONS];
  63. static DWORD ddeInst;            /* Instance of this process */
  64.  
  65. static Module     MODULE_dde;        /* win_dde */
  66. static functor_t FUNCTOR_dde_connect3;
  67. static functor_t FUNCTOR_dde_connect_confirm3;
  68. static functor_t FUNCTOR_dde_disconnect1;
  69. static functor_t FUNCTOR_dde_request4;
  70. static functor_t FUNCTOR_dde_execute3;
  71. static functor_t FUNCTOR_error1;
  72.  
  73. static char *
  74. dde_error_message(int errn)
  75. { char *err;
  76.  
  77.   if ( errn <= 0 )
  78.     errn = DdeGetLastError(ddeInst);
  79.  
  80.   switch(errn)
  81.   { case DMLERR_ADVACKTIMEOUT:
  82.     case DMLERR_DATAACKTIMEOUT:        
  83.     case DMLERR_EXECACKTIMEOUT:
  84.     case DMLERR_POKEACKTIMEOUT:
  85.     case DMLERR_UNADVACKTIMEOUT:    err = "Timeout";        break;
  86.     case DMLERR_BUSY:            err = "Service busy";        break;
  87.     case DMLERR_DLL_NOT_INITIALIZED:    err = "DDL not initialised";    break;
  88.     case DMLERR_INVALIDPARAMETER:    err = "Invalid parameter";    break;
  89.     case DMLERR_MEMORY_ERROR:        err = "Memory error";        break;
  90.     case DMLERR_NO_CONV_ESTABLISHED:    err = "No conversation";    break;
  91.     case DMLERR_NO_ERROR:        err = "No error???";        break;
  92.     case DMLERR_NOTPROCESSED:        err = "Not processed";        break;
  93.     case DMLERR_POSTMSG_FAILED:        err = "PostMessage() failed";    break;
  94.     case DMLERR_REENTRANCY:        err = "Reentrance";        break;
  95.     case DMLERR_SERVER_DIED:        err = "Server died";        break;
  96.     default:                err = "Unknown error";        break;
  97.   }
  98.  
  99.   return err;
  100. }
  101.  
  102.  
  103. static word
  104. dde_warning(char *cmd)
  105. { char *err = dde_error_message(-1);
  106.  
  107.   return warning("%s: DDE operation failed: %s", cmd, err);
  108. }
  109.  
  110.  
  111. static atom_t
  112. hszToAtom(HSZ hsz)
  113. { char buf[FASTBUFSIZE];
  114.   int len;
  115.  
  116.   if ( !(len=DdeQueryString(ddeInst, hsz, buf, sizeof(buf)-1, CP_WINANSI)) )
  117.   { dde_warning("string handle");
  118.     return NULL_ATOM;
  119.   }
  120.  
  121.   if ( len == sizeof(buf)-1 )
  122.   { if ( (len=DdeQueryString(ddeInst, hsz, NULL, 0, CP_WINANSI)) > 0 )
  123.     { char *b2 = malloc(len+1);
  124.       atom_t a;
  125.       
  126.       DdeQueryString(ddeInst, hsz, b2, len+1, CP_WINANSI);
  127.       a = lookupAtom(b2);
  128.       free(b2);
  129.  
  130.       return a;
  131.     }
  132.  
  133.     dde_warning("string handle");
  134.   }
  135.  
  136.   return lookupAtom(buf);
  137. }
  138.  
  139.  
  140. static int
  141. unify_hdata(term_t t, HDDEDATA data)
  142. { char buf[FASTBUFSIZE];
  143.   int len;
  144.  
  145.   if ( !(len=DdeGetData(data, buf, sizeof(buf)-1, 0)) )
  146.   { dde_warning("data handle");
  147.     fail;
  148.   }
  149.  
  150.   if ( len == sizeof(buf)-1 )
  151.   { if ( (len=DdeGetData(data, NULL, 0, 0)) > 0 )
  152.     { char *b2 = malloc(len+1);
  153.       int rval;
  154.       
  155.       DdeGetData(data, b2, len, 0);
  156.       b2[len] = '\0';
  157.       rval = PL_unify_list_chars(t, b2);
  158.       free(b2);
  159.  
  160.       return rval;
  161.     }
  162.  
  163.     dde_warning("data handle");
  164.   }
  165.  
  166.   return PL_unify_list_chars(t, buf);
  167. }
  168.  
  169.  
  170. static int
  171. get_hsz(term_t data, HSZ *rval)
  172. { char *s;
  173.  
  174.   if ( PL_get_chars(data, &s, CVT_ALL) )
  175.   { HSZ h = DdeCreateStringHandle(ddeInst, s, CP_WINANSI);
  176.     if ( h )
  177.     { *rval = h;
  178.       succeed;
  179.     }
  180.   }
  181.  
  182.   fail;
  183. }
  184.  
  185.  
  186. static int
  187. allocServerHandle(HCONV handle)
  188. { int i;
  189.  
  190.   for(i=0; i<MAX_CONVERSATIONS; i++)
  191.   { if ( !server_handle[i] )
  192.     { server_handle[i] = handle;
  193.       return i;
  194.     }
  195.   }
  196.  
  197.   return -1;
  198. }
  199.  
  200.  
  201. static int
  202. findServerHandle(HCONV handle)
  203. { int i;
  204.  
  205.   for(i=0; i<MAX_CONVERSATIONS; i++)
  206.   { if ( server_handle[i] == handle )
  207.       return i;
  208.   }
  209.  
  210.   return -1;
  211. }
  212.  
  213.  
  214. static HDDEDATA CALLBACK 
  215. DdeCallback(UINT type, UINT fmt, HCONV hconv, HSZ hsz1, HSZ hsz2,
  216.             HDDEDATA hData, DWORD dwData1, DWORD dwData2)
  217. {
  218.   switch(type)
  219.   {  case XTYP_CONNECT:
  220.      { fid_t cid = PL_open_foreign_frame();
  221.        term_t argv = PL_new_term_refs(3);
  222.        predicate_t pred = PL_pred(FUNCTOR_dde_connect3, MODULE_dde);
  223.        int rval;
  224.  
  225.        PL_put_atom(   argv+0, hszToAtom(hsz2)); /* topic */
  226.        PL_put_atom(   argv+1, hszToAtom(hsz1)); /* service */
  227.        PL_put_integer(argv+2, dwData2 ? 1 : 0); /* same instance */
  228.        rval = PL_call_predicate(MODULE_dde, TRUE, pred, argv);
  229.        PL_discard_foreign_frame(cid);
  230.  
  231.        return (void *)rval;
  232.      }
  233.      case XTYP_CONNECT_CONFIRM:
  234.      { fid_t cid = PL_open_foreign_frame();
  235.        term_t argv = PL_new_term_refs(3);
  236.        predicate_t pred = PL_pred(FUNCTOR_dde_connect_confirm3, MODULE_dde);
  237.        int plhandle;
  238.  
  239.        if ( (plhandle = allocServerHandle(hconv)) >= 0 )
  240.        { fid_t cid = PL_open_foreign_frame();
  241.      term_t argv = PL_new_term_refs(3);
  242.      predicate_t pred = PL_pred(FUNCTOR_dde_connect_confirm3, MODULE_dde);
  243.  
  244.      PL_put_atom(   argv+0, hszToAtom(hsz2)); /* topic */
  245.      PL_put_atom(   argv+1, hszToAtom(hsz1)); /* service */
  246.      PL_put_integer(argv+2, plhandle);
  247.  
  248.      PL_call_predicate(MODULE_dde, TRUE, pred, argv);
  249.      PL_discard_foreign_frame(cid);
  250.        } else
  251.        { warning("No more DDE server handles");
  252.        }
  253.  
  254.        return NULL;
  255.      }
  256.      case XTYP_DISCONNECT:
  257.      { fid_t cid = PL_open_foreign_frame();
  258.        term_t argv = PL_new_term_refs(1);
  259.        predicate_t pred = PL_pred(FUNCTOR_dde_disconnect1, MODULE_dde);
  260.        int plhandle = findServerHandle(hconv);
  261.        
  262.        if ( plhandle >= 0 && plhandle < MAX_CONVERSATIONS )
  263.      server_handle[plhandle] = (HCONV)NULL;
  264.  
  265.        PL_put_integer(argv+0, plhandle);
  266.        PL_call_predicate(MODULE_dde, TRUE, pred, argv);
  267.        PL_discard_foreign_frame(cid);
  268.  
  269.        return NULL;
  270.      }
  271.      case XTYP_EXECUTE:
  272.      { int plhandle = findServerHandle(hconv);
  273.        HDDEDATA rval = DDE_FNOTPROCESSED;
  274.        fid_t cid = PL_open_foreign_frame();
  275.        term_t argv = PL_new_term_refs(3);
  276.        predicate_t pred = PL_pred(FUNCTOR_dde_execute3, MODULE_dde);
  277.  
  278.        PL_put_integer(argv+0, plhandle);
  279.        PL_put_atom(   argv+1, hszToAtom(hsz1));
  280.        unify_hdata(   argv+2, hData);
  281.        if ( PL_call_predicate(MODULE_dde, TRUE, pred, argv) )
  282.      rval = (void *) DDE_FACK;
  283.        PL_discard_foreign_frame(cid);
  284.        DdeFreeDataHandle(hData);
  285.        return rval;
  286.      }
  287.      case XTYP_REQUEST:
  288.      { HDDEDATA data = (HDDEDATA) NULL;
  289.  
  290.        if ( fmt == CF_TEXT )
  291.        { fid_t cid = PL_open_foreign_frame();
  292.      term_t argv = PL_new_term_refs(4);
  293.      predicate_t pred = PL_pred(FUNCTOR_dde_request4, MODULE_dde);
  294.      int plhandle = findServerHandle(hconv);
  295.  
  296.      PL_put_integer( argv+0, plhandle);
  297.      PL_put_atom(     argv+1, hszToAtom(hsz1)); /* topic */
  298.      PL_put_atom(    argv+2, hszToAtom(hsz2)); /* item */
  299.      PL_put_variable(argv+3);
  300.  
  301.      if ( PL_call_predicate(MODULE_dde, TRUE, pred, argv) )
  302.      { char *s;
  303.  
  304.        if ( PL_get_chars(argv+3, &s, CVT_ALL) )
  305.          data = DdeCreateDataHandle(ddeInst, s, strlen(s)+1,
  306.                     0, hsz2, CF_TEXT, 0);
  307.      }
  308.      PL_discard_foreign_frame(cid);
  309.        }
  310.  
  311.        return data;
  312.      }
  313.      default:
  314.        ;
  315.   }
  316.  
  317.   return (HDDEDATA)NULL;
  318. }
  319.  
  320.  
  321. static int
  322. dde_initialise()
  323. { if ( ddeInst == (DWORD)NULL )
  324.   { if (DdeInitialize(&ddeInst, (PFNCALLBACK)DdeCallback,
  325.               APPCLASS_STANDARD|CBF_FAIL_ADVISES|CBF_FAIL_POKES|
  326.               CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,
  327.               0L)
  328.     != DMLERR_NO_ERROR)
  329.     { ddeInst = (DWORD) -1;
  330.       return dde_warning("initialise");
  331.     }
  332.  
  333.     MODULE_dde = lookupModule(lookupAtom("win_dde"));
  334.  
  335.     FUNCTOR_dde_connect3  =
  336.     lookupFunctorDef(lookupAtom("$dde_connect"), 3);
  337.     FUNCTOR_dde_connect_confirm3 =
  338.     lookupFunctorDef(lookupAtom("$dde_connect_confirm"), 3);
  339.     FUNCTOR_dde_disconnect1 =
  340.         lookupFunctorDef(lookupAtom("$dde_disconnect"), 1);
  341.     FUNCTOR_dde_request4  =
  342.     lookupFunctorDef(lookupAtom("$dde_request"), 4);
  343.     FUNCTOR_dde_execute3  =
  344.     lookupFunctorDef(lookupAtom("$dde_execute"), 3);
  345.     FUNCTOR_error1        =
  346.         lookupFunctorDef(lookupAtom("error"), 1);
  347.   }
  348.  
  349.   succeed;
  350. }
  351.  
  352.  
  353. word
  354. pl_dde_register_service(term_t topic, term_t onoff)
  355. { HSZ t;
  356.   atom_t a;
  357.  
  358.   TRY(dde_initialise());
  359.  
  360.   if ( !get_hsz(topic, &t) ||
  361.        !PL_get_atom(onoff, &a) )
  362.     return warning("dde_register_topic/1: instantiation fault");
  363.  
  364.   if ( a == ATOM_off )
  365.   { int rval = (int)DdeNameService(ddeInst, t, 0L, DNS_UNREGISTER);
  366.     DdeFreeStringHandle(ddeInst, t);
  367.     return rval ? TRUE : FALSE;
  368.   } else
  369.   { if ( DdeNameService(ddeInst, t, 0L, DNS_REGISTER|DNS_FILTERON) )
  370.       succeed;                /* should we free too? */
  371.  
  372.     DdeFreeStringHandle(ddeInst, t);
  373.     return dde_warning("dde_register_request");
  374.   }
  375. }
  376.  
  377.  
  378. word
  379. pl_open_dde_conversation(term_t service, term_t topic,
  380.              term_t handle)
  381. { UINT i;
  382.   HSZ Hservice, Htopic;
  383.  
  384.   if ( !dde_initialise() )
  385.     fail;
  386.  
  387.   if ( !get_hsz(service, &Hservice) ||
  388.        !get_hsz(topic, &Htopic) )
  389.     return warning("open_dde_conversation/3: instantion fault");
  390.  
  391.   /* Establish a connection and get a handle for it */
  392.   for (i=0; i < MAX_CONVERSATIONS; i++)   /* Find an open slot */
  393.   { if (conv_handle[i] == (HCONV)NULL)
  394.       break;
  395.   }
  396.   if (i == MAX_CONVERSATIONS)
  397.     return warning("open_dde_conversation/3: too many conversations");
  398.  
  399.   if ( !(conv_handle[i] = DdeConnect(ddeInst, Hservice, Htopic, 0)) )
  400.     fail;
  401.  
  402.   DdeFreeStringHandle(ddeInst, Hservice);
  403.   DdeFreeStringHandle(ddeInst, Htopic);
  404.  
  405.   return PL_unify_integer(handle, i);
  406. }
  407.  
  408.  
  409. static int
  410. get_conv_handle(term_t handle, int *theh)
  411. { int h;
  412.  
  413.   if ( PL_get_integer(handle, &h) &&
  414.        h >= 0 && h < MAX_CONVERSATIONS &&
  415.        conv_handle[h] )
  416.   { *theh = h;
  417.  
  418.     succeed;
  419.   }
  420.  
  421.   fail;
  422. }
  423.  
  424.  
  425. word
  426. pl_close_dde_conversation(term_t handle)
  427. { int hdl;
  428.  
  429.   if ( !get_conv_handle(handle, &hdl) )
  430.     return warning("close_dde_conversation/1: invalid handle");
  431.  
  432.   DdeDisconnect(conv_handle[hdl]);
  433.   conv_handle[hdl] = (HCONV)NULL;
  434.  
  435.   succeed;
  436. }
  437.  
  438.  
  439. word
  440. pl_dde_request(term_t handle, term_t item,
  441.            term_t value, term_t timeout)
  442. { int hdl;
  443.   int rval;
  444.   int ddeErr;
  445.   HSZ Hitem;
  446.   DWORD result, valuelen;
  447.   HDDEDATA Hvalue;
  448.   long tmo;
  449.  
  450.   if ( !get_conv_handle(handle, &hdl) )
  451.     return warning("dde_request/4: invalid handle");
  452.   if ( !get_hsz(item, &Hitem) )
  453.     return warning("dde_request/4: invalid item");
  454.   if ( !PL_get_long(timeout, &tmo) )
  455.     return warning("dde_request/4: invalid timeout");
  456.  
  457.   if ( tmo <= 0 )
  458.     tmo = TIMEOUT_VERY_LONG;
  459.  
  460.   Hvalue = DdeClientTransaction(NULL, 0, conv_handle[hdl], Hitem, CF_TEXT,
  461.                 XTYP_REQUEST, (DWORD)tmo, &result);
  462.   ddeErr = DdeGetLastError(ddeInst);
  463.   DdeFreeStringHandle(ddeInst, Hitem);
  464.  
  465.   if ( Hvalue)
  466.   { char * valuebuf;
  467.     char * valuedata;
  468.     valuedata = DdeAccessData(Hvalue, &valuelen);
  469.     valuebuf = (char *)malloc((size_t)valuelen+1);
  470.     strncpy(valuebuf, valuedata, valuelen+1);
  471.     DdeUnaccessData(Hvalue);
  472.     valuebuf[valuelen] = EOS;
  473.     rval = PL_unify_string_chars(value, valuebuf);
  474.     free(valuebuf);
  475.     return rval;
  476.   } else
  477.   { char * errmsg = dde_error_message(ddeErr);
  478.  
  479.     return PL_unify_term(value,
  480.              PL_FUNCTOR, FUNCTOR_error1, /* error(Message) */
  481.              PL_CHARS,   errmsg);
  482.   }
  483. }
  484.  
  485.  
  486.  
  487. word
  488. pl_dde_execute(term_t handle, term_t command, term_t timeout)
  489. { int hdl;
  490.   char *cmdstr;
  491.   HDDEDATA Hvalue, data;
  492.   DWORD result;
  493.   long tmo;
  494.  
  495.   if ( !get_conv_handle(handle, &hdl) )
  496.     return warning("dde_execute/3: invalid handle");
  497.   if ( !PL_get_chars(command, &cmdstr, CVT_ALL) )
  498.     return warning("dde_execute/3: invalid command");
  499.   if ( !PL_get_long(timeout, &tmo) )
  500.     return warning("dde_execute/2: invalid timeout");
  501.  
  502.   if ( tmo <= 0 )
  503.     tmo = TIMEOUT_VERY_LONG;
  504.  
  505.   if ( !(data = DdeCreateDataHandle(ddeInst, cmdstr, strlen(cmdstr)+1,
  506.                     0, 0, CF_TEXT, 0)) )
  507.     return dde_warning("dde_execute/3");
  508.  
  509.   Hvalue = DdeClientTransaction((LPBYTE) data, (DWORD) -1,
  510.                 conv_handle[hdl], 0L, 0,
  511.                 XTYP_EXECUTE, (DWORD) tmo, &result);
  512.   if ( Hvalue )
  513.     succeed;
  514.  
  515.   return dde_warning("dde_execute/2");
  516. }
  517.  
  518.  
  519. word
  520. pl_dde_poke(term_t handle, term_t item, term_t data, term_t timeout)
  521. { int hdl;
  522.   char *datastr;
  523.   HDDEDATA Hvalue;
  524.   HSZ Hitem;
  525.   long tmo;
  526.  
  527.   if ( !get_conv_handle(handle, &hdl) )
  528.     return warning("dde_poke/4: invalid handle");
  529.   if ( !get_hsz(item, &Hitem) )
  530.     return warning("dde_poke/4: invalid item");
  531.   if ( !PL_get_chars(data, &datastr, CVT_ALL) )
  532.     return warning("dde_poke/4: invalid data");
  533.   if ( !PL_get_long(timeout, &tmo) )
  534.     return warning("dde_poke/4: invalid timeout");
  535.  
  536.   if ( tmo <= 0 )
  537.     tmo = TIMEOUT_VERY_LONG;
  538.  
  539.   Hvalue = DdeClientTransaction(datastr, strlen(datastr)+1,
  540.                 conv_handle[hdl], Hitem, CF_TEXT,
  541.                 XTYP_POKE, (DWORD)tmo, NULL);
  542.   if ( !Hvalue )
  543.     return dde_warning("dde_poke/2");
  544.  
  545.   succeed;
  546. }
  547.  
  548. #endif /*O_DDE*/
  549. #endif /*__WINDOWS__*/
  550.